#pragma comment (lib, "glew32.lib")
#pragma comment (lib, "freeglut.lib")

#include <windows.h>
#include "GL\glew.h"
#include <GL\freeglut.h>  

#include <stdlib.h>
#include <stdio.h>
#include <boost/smart_ptr.hpp>
#include "Input.hpp"
#include "Timer.hpp"
#include "GameManager.hpp"

boost::shared_ptr<GameManager> gm;
double lastframetime;
int window;

bool keyPressed[30];

int mousePosX, mousePosY; // where the mouse is
int oldMousX, oldMousY; // where it was last frame
float mouseSpeedX, mouseSpeedY; // difference

void CheckFrameBufferStatus()
{
	GLenum eStatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);	
	switch (eStatus)
	{
		// everything is OK
	case GL_FRAMEBUFFER_COMPLETE_EXT:
		break;

		// Unsupported Format
	case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
		std::cout << "Error: Unsupported Format" << std::endl;
		break;

		// Other Error
	default:
		assert(0);
		break;
	}
}

void init()
{
  GLenum err = glewInit(); 
  if (GLEW_OK != err )
  {
	fprintf(stderr, "error: %s\n", glewGetErrorString(err) );
	exit(0);
  }
  CheckFrameBufferStatus();


  glClearColor(0.0, 0.0, 0.0, 0.0);
  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);

  lastframetime = currentTime();

  gm.reset(new GameManager());
  gm->init();

  mouseSpeedX = 0.0f;
  mouseSpeedY = 0.0f;

  for(int i=0; i<30; i++)
    keyPressed[i]=false;
}

void display()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  double now = currentTime();
  double sslf = now - lastframetime; 
  gm->update(sslf); // \breif pass in deltatime. dt = sslf = seconds since last frame
  gm->render();

  // After rendering each frame, check for GL Errors:
  //GLenum errCode;
  //errCode = glGetError();
  //if (errCode != GL_NO_ERROR)
  //{
	 //  fprintf(stderr, "error: %s\n", glewGetErrorString(errCode) );
  //}

  GLenum errCode;
  errCode = glGetError();
  if( errCode != GL_NO_ERROR)
  {
	std::cout << "GL errors this frame: " << std::endl;
	while (errCode != GL_NO_ERROR)
	{
		std::cout << gluErrorString(errCode) << std::endl;
		errCode = glGetError();
	}
  }



  lastframetime = now;

  mouseSpeedX = (mousePosX - oldMousX) * (float)sslf;
  mouseSpeedY = (mousePosY - oldMousY) * (float)sslf;
  oldMousX = mousePosX;
  oldMousY = mousePosY;

  float speed = 2;

  if(keyPressed[KEY_ID_C]==true)     {keyPressed[KEY_ID_C] = false; gm->spawnMinion(); }
  if(keyPressed[KEY_ID_W]==true)      gm->getCam()->moveForward(speed);
  if(keyPressed[KEY_ID_A]==true)      gm->getCam()->moveLeft(speed);
  if(keyPressed[KEY_ID_D]==true)      gm->getCam()->moveRight(speed);
  if(keyPressed[KEY_ID_S]==true)      gm->getCam()->moveBackward(speed);
  //if(keyPressed[KEY_ID_SPACE]==true)  gm->getCam()->moveUp(speed);
  
  if(keyPressed[MOUSE_LEFT_BUTTON_DOWN])
  {
	  gm->getCam()->setRot( -mouseSpeedX, mouseSpeedY );
  }

  gm->getBattleField()->setPickPoint(mousePosX, mousePosY);
  if(keyPressed[KEY_ID_SPACE])
  {
	//keyPressed[KEY_ID_SPACE] = false;
	
	gm->placeTower();
  }

  glutSwapBuffers();
  glutPostRedisplay();
}

void keyDown(unsigned char key, int x, int y)
{
  switch (key) 
  {
    case 'q':
    case 27:
      glutDestroyWindow(window);
#ifndef _WIN32
      // Must use this with regular glut, since it never returns control to main().
      exit(0);
#endif
      break;
      
    case 'w':
      keyPressed[KEY_ID_W] = true;
      break;
    case 'a':
      keyPressed[KEY_ID_A] = true;
      break;
    case 's':
      keyPressed[KEY_ID_S] = true;
      break;
    case 'd':
      keyPressed[KEY_ID_D] = true;
      break;
    case ' ':
      keyPressed[KEY_ID_SPACE] = true;
      break;
    case 'c':
      keyPressed[KEY_ID_C] = true;
      break;

    default:
      glutPostRedisplay();
  }
}

void keyUp(unsigned char key, int x, int y)
{
  switch (key) 
  {
    case 'w':
      keyPressed[KEY_ID_W] = false;
      break;
    case 'a':
      keyPressed[KEY_ID_A] = false;
      break;
    case 's':
      keyPressed[KEY_ID_S] = false;
      break;
    case 'd':
      keyPressed[KEY_ID_D] = false;
      break;
    case ' ':
      keyPressed[KEY_ID_SPACE] = false;
      break;
    case 'c':
      keyPressed[KEY_ID_C] = false;
      break;
  }
}

void mousePressed(int button, int state, int posX, int posY)
{
  mousePosX = posX;
  mousePosY = posY;

  if(button==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
  {
    keyPressed[MOUSE_LEFT_BUTTON_DOWN] = true;
  }  
  if(button==GLUT_LEFT_BUTTON && state==GLUT_UP)
    keyPressed[MOUSE_LEFT_BUTTON_DOWN] = false;
}

void mouseMoved(int posX, int posY)
{
  mousePosX = posX;
  mousePosY = posY;
  // Implement quaternion based mouse move
}

void mousePassive(int posX, int posY)
{
  mousePosX = posX;
  mousePosY = posY;
}

void reshape(int w, int h)
{
  glViewport(0, 0, (GLsizei) w, (GLsizei) h); 
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(60.0f, float(w)/float(h) ,1.0f, 3000.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
//  gluLookAt(0.0, 0.0, 10.0,     0.0, 0.0, 0.0,    0.0, 1.0, 0.0);
}

int main(int argc, char** argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
  glutInitWindowSize(700, 700); 
  glutInitWindowPosition(10, 10);
  window = glutCreateWindow("Tower Defence 3D - place tower: SPACEBAR, camera movement:WASD and mouse1");
  init();
  glutKeyboardFunc(keyDown);
  glutKeyboardUpFunc(keyUp);
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutMouseFunc(mousePressed);
  glutMotionFunc(mouseMoved);
  glutPassiveMotionFunc(mousePassive);

  //glutIgnoreKeyRepeat(true);


  // Add other callback functions here..

  glutMainLoop();
  return 0;
}
